package com.zch.rpc.client.client;

import com.zch.rpc.client.common.model.RequestModel;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * 客户端服务类代理工厂
 * 
 * 1. 调用getProxy方法获取代理对象
 * 2. 代理对象的方法被调用时会被invoke方法拦截，执行invoke
 * 	 	1）封装参数，用于发送到服务器，定位服务、执行服务
 * 		2）链接服务器调用服务
 * @author QJM
 *
 */
public class RpcClientProxy implements InvocationHandler{
	private String host;
	private int port;

	public RpcClientProxy(String host, int port){
		this.host = host;
		this.port = port;
	}
	
	/**
	 * 生成代理对象
	 * @param clazz 代理类型（接口）
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public <T>T getProxy(Class<T> clazz){
		// clazz不是接口不能使用JDK动态代理
		return (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class<?>[]{clazz}, RpcClientProxy.this);
	}

	/**
	 * 咱们这里因为要做rpc，跟以往的动态代理稍微有点区别，以往的动态代理需要在这个方法里
	 * 调用被代理对象，去执行执行被代理对象的方法。但我们这里是rpc，被代理对象是在远端的获取不多，
	 * 因为我们rpc是用接口调用的，所以：
	 *         1：我们在这里将接口的类名，方法名，方法参数类型，方法参数值这些接口层面的信息
	 *         然后将这个对象序列化后通过io流 进行传输（socket通信），服务端在从流中取出进行反序列化得到接口信息，
	 *
	 * @param obj
	 * @param method
	 * @param params
	 * @return
	 * @throws Throwable
	 */
	public Object invoke(Object obj, Method method, Object[] params) throws Throwable {
		//封装参数
		RequestModel request = new RequestModel();
		request.setInterfaceName(method.getDeclaringClass().getName());
		request.setMethodName(method.getName());
		request.setParamTypes(method.getParameterTypes());
		request.setParams(params);
		//链接服务器调用服务
		RpcClient client = new RpcClient();
		return client.start(request, host, port);
	}
}
